﻿/******************************************************************************
	Cobalt - utility libraries for building XNA games
	Copyright (C) 2008 Philip Djonov

	This program is free software; you can redistribute it and/or modify it
	under the terms of the GNU General Public License as published by the Free
	Software Foundation; either version 2 of the License, or (at your option)
	any later version.

	This program is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
	or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
	for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the
	
		Free Software Foundation, Inc.
		51 Franklin Street, Fifth Floor
		Boston, MA  02110-1301, USA.
******************************************************************************/

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Graphics.PackedVector;
using Microsoft.Xna.Framework.Content.Pipeline;
using Microsoft.Xna.Framework.Content.Pipeline.Graphics;
using Microsoft.Xna.Framework.Content.Pipeline.Processors;

namespace Cobalt.Content.Pipeline.Graphics
{

	public static class MeshHelpers
	{

		public static void MergeGeometries( MeshContent mesh,
			IEnumerable<IEnumerable<GeometryContent>> mergeGroups,
			int maxVertsPerGeometry, int maxElemsPerGeometry,
			out Dictionary<GeometryContent, GeometryContent> remapping, out int numMerges )
		{
			if( mesh == null )
				throw new ArgumentNullException( "mesh" );
			if( mergeGroups == null )
				throw new ArgumentNullException( "mergeGroups" );
			if( maxVertsPerGeometry < 3 || maxElemsPerGeometry < 3 )
				throw new ArgumentOutOfRangeException();

			numMerges = 0;
			remapping = new Dictionary<GeometryContent, GeometryContent>();

			foreach( var mergeCollection in mergeGroups )
			{
				var mergeList = new List<GeometryContent>( mergeCollection );

				for( int e, s = 0; s < mergeList.Count; s = e )
				{
					var target = mergeList[s];
					
					if( target.Parent != mesh )
						throw new ArgumentException();

					remapping.Add( target, target );

					for( e = s + 1; e < mergeList.Count; e++ )
					{
						var toMerge = mergeList[e];

						if( AddGeometryToGeometry( mesh, target, toMerge,
							maxVertsPerGeometry, maxElemsPerGeometry ) )
						{
							remapping.Add( toMerge, target );
							mesh.Geometry.Remove( toMerge );

							numMerges++;
						}
						else
							break;
					}
				}
			}
		}

		public static void MergeGeometries( MeshContent mesh,
			IEnumerable<IEnumerable<GeometryContent>> mergeGroups,
			out Dictionary<GeometryContent, GeometryContent> remapping )
		{
			int numMerges;
			MergeGeometries( mesh, mergeGroups, ushort.MaxValue, int.MaxValue, out remapping, out numMerges );
		}

		private static bool AddGeometryToGeometry( MeshContent mesh, GeometryContent dst, GeometryContent src,
			int maxVerts, int maxElems )
		{
			if( src.Vertices.VertexCount + dst.Vertices.VertexCount >= maxVerts ||
				src.Indices.Count + dst.Indices.Count >= maxElems )
				return false;

			int baseVert = dst.Vertices.VertexCount;
			dst.Vertices.AddRange( src.Vertices.PositionIndices );

			foreach( int idx in src.Indices )
				dst.Indices.Add( baseVert + idx );

			foreach( VertexChannel srcChan in src.Vertices.Channels )
			{
				VertexChannel dstChan = dst.Vertices.Channels[srcChan.Name];
				if( dstChan == null )
					throw new InvalidContentException( "Mismatched source and destination channels.", mesh.Identity );

				for( int i = 0; i < srcChan.Count; i++ )
					dstChan[baseVert + i] = srcChan[i];
			}

			return true;
		}

		public static bool IsTangentBasis( VertexElementUsage usage )
		{
			switch( usage )
			{
				case VertexElementUsage.Normal:
				case VertexElementUsage.Tangent:
				case VertexElementUsage.Binormal:
					return true;

				default:
					return false;
			}
		}

		public static bool IsTangentBasis( string channelName )
		{
			VertexElementUsage usage;
			if( VertexChannelNames.TryDecodeUsage( channelName, out usage ) )
				return IsTangentBasis( usage );

			return false;
		}


		public static void SwapWindingOrder( GeometryContent geom )
		{
			for( int i = 0; i < geom.Indices.Count; i += 3 )
			{
				int num2 = geom.Indices[i];
				geom.Indices[i] = geom.Indices[i + 2];
				geom.Indices[i + 2] = num2;
			}
		}
	}

}